home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / mint96sb.zoo / src / shmfs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-10  |  14.5 KB  |  700 lines

  1. /*
  2. Copyright 1992 Eric R. Smith. All rights reserved.
  3.  */
  4.  
  5. /* Shared memory file system */
  6.  
  7. #include "mint.h"
  8.  
  9.  
  10. static long    ARGS_ON_STACK shm_root    P_((int drv, fcookie *fc));
  11. static long    ARGS_ON_STACK shm_creat    P_((fcookie *dir, const char *name, unsigned mode,
  12.                     int attrib, fcookie *fc));
  13. static long    ARGS_ON_STACK shm_lookup    P_((fcookie *dir, const char *name, fcookie *fc));
  14. static long    ARGS_ON_STACK shm_getxattr    P_((fcookie *fc, XATTR *xattr));
  15. static long    ARGS_ON_STACK shm_chattr    P_((fcookie *fc, int attrib));
  16. static long    ARGS_ON_STACK shm_chown    P_((fcookie *fc, int uid, int gid));
  17. static long    ARGS_ON_STACK shm_chmode    P_((fcookie *fc, unsigned mode));
  18. static long    ARGS_ON_STACK shm_rmdir    P_((fcookie *dir, const char *name));
  19. static long    ARGS_ON_STACK shm_remove    P_((fcookie *dir, const char *name));
  20. static long    ARGS_ON_STACK shm_getname    P_((fcookie *root, fcookie *dir, char *pathname));
  21. static long    ARGS_ON_STACK shm_rename    P_((fcookie *olddir, char *oldname,
  22.                     fcookie *newdir, const char *newname));
  23. static long    ARGS_ON_STACK shm_opendir    P_((DIR *dirh, int flags));
  24. static long    ARGS_ON_STACK shm_readdir    P_((DIR *dirh, char *nm, int nmlen, fcookie *));
  25. static long    ARGS_ON_STACK shm_rewinddir    P_((DIR *dirh));
  26. static long    ARGS_ON_STACK shm_closedir    P_((DIR *dirh));
  27. static long    ARGS_ON_STACK shm_pathconf    P_((fcookie *dir, int which));
  28. static long    ARGS_ON_STACK shm_dfree    P_((fcookie *dir, long *buf));
  29. static DEVDRV *    ARGS_ON_STACK shm_getdev    P_((fcookie *fc, long *devsp));
  30.  
  31. static long    ARGS_ON_STACK shm_open    P_((FILEPTR *f));
  32. static long    ARGS_ON_STACK shm_write    P_((FILEPTR *f, const char *buf, long bytes));
  33. static long    ARGS_ON_STACK shm_read    P_((FILEPTR *f, char *buf, long bytes));
  34. static long    ARGS_ON_STACK shm_lseek    P_((FILEPTR *f, long where, int whence));
  35. static long    ARGS_ON_STACK shm_ioctl    P_((FILEPTR *f, int mode, void *buf));
  36. static long    ARGS_ON_STACK shm_datime    P_((FILEPTR *f, short *time, int rwflag));
  37. static long    ARGS_ON_STACK shm_close    P_((FILEPTR *f, int pid));
  38.  
  39. /* dummy routines from biosfs.c */
  40. extern long    ARGS_ON_STACK null_select    P_((FILEPTR *f, long p, int mode));
  41. extern void    ARGS_ON_STACK null_unselect    P_((FILEPTR *f, long p, int mode));
  42.  
  43. static short shmtime, shmdate;
  44.  
  45. #define SHMNAME_MAX 15
  46.  
  47. typedef struct shmfile {
  48.     struct shmfile *next;
  49.     char filename[SHMNAME_MAX+1];
  50.     int uid, gid;
  51.     short time, date;
  52.     unsigned mode;
  53.     int inuse;
  54.     MEMREGION *reg;
  55. } SHMFILE;
  56.  
  57. SHMFILE *shmroot = 0;
  58.  
  59. DEVDRV shm_device = {
  60.     shm_open, shm_write, shm_read, shm_lseek, shm_ioctl, shm_datime,
  61.     shm_close, null_select, null_unselect
  62. };
  63.  
  64. FILESYS shm_filesys = {
  65.     (FILESYS *)0,
  66.     0,
  67.     shm_root,
  68.     shm_lookup, shm_creat, shm_getdev, shm_getxattr,
  69.     shm_chattr, shm_chown, shm_chmode,
  70.     nomkdir, shm_rmdir, shm_remove, shm_getname, shm_rename,
  71.     shm_opendir, shm_readdir, shm_rewinddir, shm_closedir,
  72.     shm_pathconf, shm_dfree,
  73.     nowritelabel, noreadlabel, nosymlink, noreadlink, nohardlink,
  74.     nofscntl, nodskchng
  75. };
  76.  
  77. long ARGS_ON_STACK 
  78. shm_root(drv, fc)
  79.     int drv;
  80.     fcookie *fc;
  81. {
  82.     if ((unsigned)drv == SHMDRV) {
  83.         fc->fs = &shm_filesys;
  84.         fc->dev = drv;
  85.         fc->index = 0L;
  86.         return 0;
  87.     }
  88.     fc->fs = 0;
  89.     return EINTRN;
  90. }
  91.  
  92. static long ARGS_ON_STACK 
  93. shm_lookup(dir, name, fc)
  94.     fcookie *dir;
  95.     const char *name;
  96.     fcookie *fc;
  97. {
  98.     SHMFILE *s;
  99.  
  100.     if (dir->index != 0) {
  101.         DEBUG(("shm_lookup: bad directory"));
  102.         return EPTHNF;
  103.     }
  104.  
  105. /* special case: an empty name in a directory means that directory */
  106. /* so does "." */
  107.     if (!*name || (name[0] == '.' && name[1] == 0)) {
  108.         *fc = *dir;
  109.         return 0;
  110.     }
  111.  
  112. /* another special case: ".." could be a mount point */
  113.     if (!strcmp(name, "..")) {
  114.         *fc = *dir;
  115.         return EMOUNT;
  116.     }
  117.  
  118.     for (s = shmroot; s; s = s->next) {
  119.         if (!stricmp(s->filename,name))
  120.             break;
  121.     }
  122.  
  123.     if (!s) {
  124.         DEBUG(("shm_lookup: name not found"));
  125.         return EFILNF;
  126.     } else {
  127.         fc->index = (long)s;
  128.         fc->fs = &shm_filesys;
  129.         fc->dev = SHMDRV;
  130.     }
  131.     return 0;
  132. }
  133.  
  134. static long ARGS_ON_STACK 
  135. shm_getxattr(fc, xattr)
  136.     fcookie *fc;
  137.     XATTR *xattr;
  138. {
  139.     SHMFILE *s;
  140.  
  141.     xattr->blksize = 1;
  142.     if (fc->index == 0) {
  143.         /* the root directory */
  144.         xattr->index = 0;
  145.         xattr->dev = SHMDRV;
  146.         xattr->nlink = 1;
  147.         xattr->uid = xattr->gid = 0;
  148.         xattr->size = xattr->nblocks = 0;
  149.         xattr->mtime = xattr->atime = xattr->ctime = shmtime;
  150.         xattr->mdate = xattr->adate = xattr->cdate = shmdate;
  151.         xattr->mode = S_IFDIR | DEFAULT_DIRMODE;
  152.         xattr->attr = FA_DIR;
  153.         return 0;
  154.     }
  155.  
  156.     s = (SHMFILE *)fc->index;
  157.     xattr->index = (long) s;
  158.     xattr->dev = SHMDRV;
  159.     xattr->uid = s->uid; xattr->gid = s->gid;
  160.     if (s->reg) {
  161.         xattr->size = xattr->nblocks = s->reg->len;
  162.         xattr->nlink = s->reg->links + 1;
  163.      } else {
  164.         xattr->size = xattr->nblocks = 0;
  165.         xattr->nlink = 1;
  166.     }
  167.     xattr->mtime = xattr->ctime = xattr->atime = s->time;
  168.     xattr->mdate = xattr->cdate = xattr->adate = s->date;
  169.     xattr->mode = s->mode;
  170.     xattr->attr = (s->mode & (S_IWUSR|S_IWGRP|S_IWOTH)) ? 0 : 
  171.             FA_RDONLY;
  172.     return 0;
  173. }
  174.  
  175. static long ARGS_ON_STACK 
  176. shm_chattr(fc, attrib)
  177.     fcookie *fc;
  178.     int attrib;
  179. {
  180.     SHMFILE *s;
  181.  
  182.     s = (SHMFILE *)fc->index;
  183.     if (!s) return EACCDN;
  184.  
  185.     if (attrib & FA_RDONLY) {
  186.         s->mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
  187.     } else if ( !(s->mode & (S_IWUSR|S_IWGRP|S_IWOTH)) ) {
  188.         s->mode |= (S_IWUSR|S_IWGRP|S_IWOTH);
  189.     }
  190.     return 0;
  191. }
  192.  
  193. static long ARGS_ON_STACK 
  194. shm_chown(fc, uid, gid)
  195.     fcookie *fc;
  196.     int uid, gid;
  197. {
  198.     SHMFILE *s;
  199.  
  200.     s = (SHMFILE *)fc->index;
  201.     if (!s)
  202.         return EACCDN;
  203.     s->uid = uid;
  204.     s->gid = gid;
  205.     return 0;
  206. }
  207.  
  208. static long ARGS_ON_STACK 
  209. shm_chmode(fc, mode)
  210.     fcookie *fc;
  211.     unsigned mode;
  212. {
  213.     SHMFILE *s;
  214.  
  215.     s = (SHMFILE *)fc->index;
  216.     if (!s)
  217.         return EINVFN;
  218.     s->mode = mode;
  219.     return 0;
  220. }
  221.  
  222. static long ARGS_ON_STACK 
  223. shm_rmdir(dir, name)
  224.     fcookie *dir;
  225.     const char *name;
  226. {
  227.     UNUSED(dir); UNUSED(name);
  228.  
  229.     return EPTHNF;
  230. }
  231.  
  232. static long ARGS_ON_STACK 
  233. shm_remove(dir, name)
  234.     fcookie *dir;
  235.     const char *name;
  236. {
  237.     SHMFILE *s, **old;
  238.  
  239.     if (dir->index != 0)
  240.         return EPTHNF;
  241.  
  242.     old = &shmroot;
  243.     for (s = shmroot; s; s = s->next) {
  244.         if (!stricmp(s->filename, name))
  245.             break;
  246.         old = &s->next;
  247.     }
  248.     if (!s)
  249.         return EFILNF;
  250.     if (s->inuse)
  251.         return EACCDN;
  252.     *old = s->next;
  253.  
  254.     s->reg->links--;
  255.     if (s->reg->links <= 0) {
  256.         free_region(s->reg);
  257.     }
  258.     kfree(s);
  259.     shmtime = timestamp;
  260.     shmdate = datestamp;
  261.     return 0;
  262. }
  263.  
  264. static long ARGS_ON_STACK 
  265. shm_getname(root, dir, pathname)
  266.     fcookie *root, *dir; char *pathname;
  267. {
  268.     UNUSED(root); UNUSED(dir);
  269.  
  270.     *pathname = 0;
  271.     return 0;
  272. }
  273.  
  274. static long ARGS_ON_STACK 
  275. shm_rename(olddir, oldname, newdir, newname)
  276.     fcookie *olddir;
  277.     char *oldname;
  278.     fcookie *newdir;
  279.     const char *newname;
  280. {
  281.     SHMFILE *s;
  282.  
  283.     if (olddir->index != 0 || newdir->index != 0)
  284.         return EPTHNF;
  285.  
  286. /* verify that "newname" doesn't exist */
  287.     for (s = shmroot; s; s = s->next)
  288.         if (!stricmp(s->filename, newname))
  289.             return EACCDN;
  290.  
  291.     for (s = shmroot; s; s = s->next)
  292.         if (!stricmp(s->filename, oldname))
  293.             break;
  294.     if (!s)
  295.         return EFILNF;
  296.  
  297.     strncpy(s->filename, newname, SHMNAME_MAX);
  298.     shmtime = timestamp;
  299.     shmdate = datestamp;
  300.     return 0;
  301. }
  302.  
  303. static long ARGS_ON_STACK 
  304. shm_opendir(dirh, flags)
  305.     DIR *dirh;
  306.     int flags;
  307. {
  308.     UNUSED(flags);
  309.  
  310.     dirh->index = 0;
  311.     return 0;
  312. }
  313.  
  314. static long ARGS_ON_STACK 
  315. shm_readdir(dirh, name, namelen, fc)
  316.     DIR *dirh;
  317.     char *name;
  318.     int namelen;
  319.     fcookie *fc;
  320. {
  321.     int i;
  322.     int giveindex = (dirh->flags == 0);
  323.     SHMFILE *s;
  324.  
  325.     s = shmroot;
  326.     i = dirh->index++;
  327.     while (i > 0 && s != 0) {
  328.         s = s->next;
  329.         --i;
  330.     }
  331.     if (!s)
  332.         return ENMFIL;
  333.  
  334.     fc->index = (long)s;
  335.     fc->fs = &shm_filesys;
  336.     fc->dev = SHMDRV;
  337.  
  338.     if (giveindex) {
  339.         namelen -= (int)sizeof(long);
  340.         if (namelen <= 0) return ERANGE;
  341.         *((long *)name) = (long)s;
  342.         name += sizeof(long);
  343.     }
  344.     if (namelen < strlen(s->filename))
  345.         return ENAMETOOLONG;
  346.     strcpy(name, s->filename);
  347.     return 0;
  348. }
  349.  
  350. static long ARGS_ON_STACK 
  351. shm_rewinddir(dirh)
  352.     DIR *dirh;
  353. {
  354.     dirh->index = 0;
  355.     return 0;
  356. }
  357.  
  358. static long ARGS_ON_STACK 
  359. shm_closedir(dirh)
  360.     DIR *dirh;
  361. {
  362.     UNUSED(dirh);
  363.     return 0;
  364. }
  365.  
  366. static long ARGS_ON_STACK 
  367. shm_pathconf(dir, which)
  368.     fcookie *dir;
  369.     int which;
  370. {
  371.     UNUSED(dir);
  372.  
  373.     switch(which) {
  374.     case -1:
  375.         return DP_MAXREQ;
  376.     case DP_IOPEN:
  377.         return UNLIMITED;    /* no internal limit on open files */
  378.     case DP_MAXLINKS:
  379.         return 1;        /* we don't have hard links */
  380.     case DP_PATHMAX:
  381.         return PATH_MAX;    /* max. path length */
  382.     case DP_NAMEMAX:
  383.         return SHMNAME_MAX;    /* max. length of individual name */
  384.     case DP_ATOMIC:
  385.         return UNLIMITED;    /* all writes are atomic */
  386.     case DP_TRUNC:
  387.         return DP_AUTOTRUNC;    /* file names are truncated */
  388.     case DP_CASE:
  389.         return DP_CASEINSENS;    /* case preserved, but ignored */
  390.     default:
  391.         return EINVFN;
  392.     }
  393. }
  394.  
  395. static long ARGS_ON_STACK 
  396. shm_dfree(dir, buf)
  397.     fcookie *dir;
  398.     long *buf;
  399. {
  400.     long size;
  401. /* "sector" size is the size of the smallest amount of memory that can be
  402.    allocated. see mem.h for the definition of ROUND
  403.  */
  404.     long secsiz = ROUND(1);
  405.  
  406.     UNUSED(dir);
  407.  
  408.     size = tot_rsize(core, 0) + tot_rsize(alt, 0);
  409.     *buf++ = size/secsiz;            /* number of free clusters */
  410.     size = tot_rsize(core, 1) + tot_rsize(alt, 1);
  411.     *buf++ = size/secsiz;            /* total number of clusters */
  412.     *buf++ = secsiz;            /* sector size (bytes) */
  413.     *buf = 1;                /* cluster size (in sectors) */
  414.     return 0;
  415. }
  416.  
  417. static DEVDRV * ARGS_ON_STACK 
  418. shm_getdev(fc, devsp)
  419.     fcookie *fc;
  420.     long *devsp;
  421. {
  422.     SHMFILE *s;
  423.  
  424.     s = (SHMFILE *)fc->index;
  425.  
  426.     *devsp = (long)s;
  427.     return &shm_device;
  428. }
  429.  
  430. /*
  431.  * create a shared memory region
  432.  */
  433.  
  434. static long ARGS_ON_STACK 
  435. shm_creat(dir, name, mode, attrib, fc)
  436.     fcookie *dir;
  437.     const char *name;
  438.     unsigned mode;
  439.     int attrib;
  440.     fcookie *fc;
  441. {
  442.     SHMFILE *s;
  443.  
  444.     UNUSED(attrib);
  445. /*
  446.  * see if the name already exists
  447.  */
  448.     for (s = shmroot; s; s = s->next) {
  449.         if (!stricmp(s->filename, name)) {
  450.             DEBUG(("shm_creat: file exists"));
  451.             return EACCDN;
  452.         }
  453.     }
  454.  
  455.     s = (SHMFILE *)kmalloc(SIZEOF(SHMFILE));
  456.     if (!s)
  457.         return ENSMEM;
  458.  
  459.     s->inuse = 0;
  460.     strncpy(s->filename, name, SHMNAME_MAX);
  461.     s->filename[SHMNAME_MAX] = 0;
  462.     s->uid = curproc->ruid;
  463.     s->gid = curproc->rgid;
  464.     s->mode = mode;
  465.     s->next = shmroot;
  466.     s->reg = 0;
  467.     s->time = shmtime = timestamp;
  468.     s->date = shmdate = datestamp;
  469.     shmroot = s;
  470.  
  471.     fc->fs = &shm_filesys;
  472.     fc->index = (long)s;
  473.     fc->dev = dir->dev;
  474.  
  475.     return 0;
  476. }
  477.  
  478. /*
  479.  * Shared memory device driver
  480.  */
  481.  
  482. /*
  483.  * BUG: file locking and the O_SHMODE restrictions are not implemented
  484.  * for shared memory
  485.  */
  486.  
  487. static long ARGS_ON_STACK 
  488. shm_open(f)
  489.     FILEPTR *f;
  490. {
  491.     SHMFILE *s;
  492.  
  493.     s = (SHMFILE *)f->devinfo;
  494.     s->inuse++;
  495.     return 0;
  496. }
  497.  
  498. static long ARGS_ON_STACK 
  499. shm_write(f, buf, nbytes)
  500.     FILEPTR *f; const char *buf; long nbytes;
  501. {
  502.     SHMFILE *s;
  503.     char *where;
  504.     long bytes_written = 0;
  505.  
  506.     s = (SHMFILE *)f->devinfo;
  507.     if (!s->reg)
  508.         return 0;
  509.  
  510.     if (nbytes + f->pos > s->reg->len)
  511.         nbytes = s->reg->len - f->pos;
  512.  
  513.     where = (char *)s->reg->loc + f->pos;
  514.  
  515. /* BUG: memory read/writes should check for valid addresses */
  516.  
  517. TRACE(("shm_write: %ld bytes to %lx", nbytes, where));
  518.  
  519.     while (nbytes-- > 0) {
  520.         *where++ = *buf++;
  521.         bytes_written++;
  522.     }
  523.     f->pos += bytes_written;
  524.     s->time = timestamp;
  525.     s->date = datestamp;
  526.     return bytes_written;
  527. }
  528.  
  529. static long ARGS_ON_STACK 
  530. shm_read(f, buf, nbytes)
  531.     FILEPTR *f; char *buf; long nbytes;
  532. {
  533.     SHMFILE *s;
  534.     char *where;
  535.     long bytes_read = 0;
  536.  
  537.     s = (SHMFILE *)f->devinfo;
  538.     if (!(s->reg))
  539.         return 0;
  540.  
  541.     if (nbytes + f->pos > s->reg->len)
  542.         nbytes = s->reg->len - f->pos;
  543.  
  544.     where = (char *)s->reg->loc + f->pos;
  545.  
  546. TRACE(("shm_read: %ld bytes from %lx", nbytes, where));
  547.  
  548.     while (nbytes-- > 0) {
  549.         *buf++ = *where++;
  550.         bytes_read++;
  551.     }
  552.     f->pos += bytes_read;
  553.     return bytes_read;
  554. }
  555.  
  556. /*
  557.  * shm_ioctl: currently, the only IOCTL's available are:
  558.  * SHMSETBLK:  set the address of the shared memory file. This
  559.  *             call may only be made once per region, and then only
  560.  *           if the region is open for writing.
  561.  * SHMGETBLK:  get the address of the shared memory region. This
  562.  *             call fails (returns 0) if SHMSETBLK has not been
  563.  *             called yet for this shared memory file.
  564.  */
  565.  
  566. static long ARGS_ON_STACK 
  567. shm_ioctl(f, mode, buf)
  568.     FILEPTR *f; int mode; void *buf;
  569. {
  570.     SHMFILE *s;
  571.     MEMREGION *m;
  572.     int i;
  573.     long r;
  574.  
  575.     s = (SHMFILE *)f->devinfo;
  576.     switch(mode) {
  577.     case SHMSETBLK:
  578.         if (s->reg) {
  579.             DEBUG(("Fcntl: SHMSETBLK already performed for %s",
  580.                 s->filename));
  581.             return ERANGE;
  582.         }
  583.         if ((f->flags & O_RWMODE) == O_RDONLY) {
  584.             DEBUG(("Fcntl: SHMSETBLK: %s was opened read-only",
  585.                 s->filename));
  586.             return EACCDN;
  587.         }
  588.     /* find the memory region to be attached */
  589.         m = 0;
  590.         for (i = curproc->num_reg - 1; i >= 0; i--) {
  591.             if (curproc->addr[i] == (virtaddr)buf) {
  592.                 m = curproc->mem[i];
  593.                 break;
  594.             }
  595.         }
  596.         if (!m || !buf) {
  597.             DEBUG(("Fcntl: SHMSETBLK: bad address %lx", buf));
  598.             return EIMBA;
  599.         }
  600.         m->links++;
  601.         s->reg = m;
  602.         return 0;
  603.     case SHMGETBLK:
  604.         if ((m = s->reg) == 0) {
  605.             DEBUG(("Fcntl: no address for SHMGETBLK"));
  606.             return 0;
  607.         }
  608.     /* check for memory limits */
  609.         if (curproc->maxmem) {
  610.             if (m->len > curproc->maxmem - memused(curproc)) {
  611.                 DEBUG(("Fcntl: SHMGETBLK would violate memory limits"));
  612.                 return 0;
  613.             }
  614.         }
  615.         return (long)attach_region(curproc, m);
  616.     case FIONREAD:
  617.     case FIONWRITE:
  618.         if (s->reg == 0) {
  619.             r = 0;
  620.         } else {
  621.             r = s->reg->len - f->pos;
  622.             if (r < 0) r = 0;
  623.         }
  624.         *((long *)buf) = r;
  625.         return 0;
  626.     default:
  627.         DEBUG(("shmfs: bad Fcntl command"));
  628.     }
  629.     return EINVFN;
  630. }
  631.  
  632. static long ARGS_ON_STACK 
  633. shm_lseek(f, where, whence)
  634.     FILEPTR *f; long where; int whence;
  635. {
  636.     long newpos, maxpos;
  637.     SHMFILE *s;
  638.  
  639.     s = (SHMFILE *)f->devinfo;
  640.  
  641.     if (s->reg)
  642.         maxpos = s->reg->len;
  643.     else
  644.         maxpos = 0;
  645.  
  646.     switch(whence) {
  647.     case 0:
  648.         newpos = where;
  649.         break;
  650.     case 1:
  651.         newpos = f->pos + where;
  652.         break;
  653.     case 2:
  654.         newpos = maxpos - where;
  655.         break;
  656.     default:
  657.         return EINVFN;
  658.     }
  659.  
  660.     if (newpos < 0 || newpos > maxpos)
  661.         return ERANGE;
  662.  
  663.     f->pos = newpos;
  664.     return newpos;
  665. }
  666.  
  667. static long ARGS_ON_STACK 
  668. shm_datime(f, timeptr, rwflag)
  669.     FILEPTR *f;
  670.     short *timeptr;
  671.     int rwflag;
  672. {
  673.     SHMFILE *s;
  674.  
  675.     s = (SHMFILE *)f->devinfo;
  676.     if (rwflag) {
  677.         s->time = *timeptr++;
  678.         s->date = *timeptr;
  679.     } else {
  680.         *timeptr++ = s->time;
  681.         *timeptr = s->date;
  682.     }
  683.     return 0;
  684. }
  685.  
  686. static long ARGS_ON_STACK 
  687. shm_close(f, pid)
  688.     FILEPTR *f;
  689.     int pid;
  690. {
  691.     SHMFILE *s;
  692.  
  693.     UNUSED(pid);
  694.     if (f->links <= 0) {
  695.         s = (SHMFILE *)f->devinfo;
  696.         s->inuse--;
  697.     }
  698.     return 0;
  699. }
  700.